package com.cn.route.md5;

import com.cn.util.Md5Hash;
import com.cn.util.SnowflakeIdWorkerUtil;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * MD5 哈希环路由
 */
public class MD5Constant {

    /**
     * 虚拟节点数目
     */
    private final int virtualCount;

    /**
     * MD5 hash
     */
    private Md5Hash md5Hash;

    /**
     * 哈希环
     */
    private SortedMap<Long, VirtualNode> ring = new TreeMap<>();

    public MD5Constant(int virtualCount) {
        this.virtualCount = virtualCount;
        this.md5Hash = new Md5Hash();
    }

    public MD5Constant(int virtualCount, List<Node> nodes) {
        this.virtualCount = virtualCount;
        this.md5Hash = new Md5Hash();
        for (Node node : nodes) {
            this.addNode(node);
        }
    }

    /**
     * 创建虚拟节点数
     *
     * @param node
     */
    public void addNode(Node node) {
        for (int i = 0; i < virtualCount; i++) {
            VirtualNode virtualNode = new VirtualNode(node, i);
            ring.put(this.md5Hash.hash(virtualNode.getKey()), virtualNode);
        }
    }

    /**
     * 路由计算
     *
     * @param key
     * @return
     */
    public VirtualNode route(String key) {
        long keys = this.md5Hash.hash(key);
        SortedMap<Long, VirtualNode> tmpNode = this.ring.tailMap(keys);
        if (tmpNode == null || tmpNode.size() == 0) {
            return this.ring.get(this.ring.firstKey());
        } else {
            return tmpNode.get(tmpNode.firstKey());
        }
    }

    static class TestNode implements Node {

        private String url;

        public TestNode(String url) {
            this.url = url;
        }

        @Override
        public String getKey() {
            return this.url;
        }

        @Override
        public String toString() {
            return "TestNode{" +
                    "url='" + url + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        //测试

        //初始化4个物理节点
        List<Node> list = new ArrayList<>();
        list.add(new TestNode("0"));
        list.add(new TestNode("1"));
        list.add(new TestNode("2"));
        list.add(new TestNode("3"));
        list.add(new TestNode("4"));
        list.add(new TestNode("5"));

        SortedMap<String, AtomicLong> countMap = new TreeMap<>();
        for (Node node : list) {
            countMap.put(node.getKey(), new AtomicLong(0));
        }

        //创建模拟用户
        List<String> users = new ArrayList<>();
        for (int i = 0; i < 20000; i++) {
            users.add(SnowflakeIdWorkerUtil.getRandom());
        }

        //通过物理节点来创建虚拟节点
        MD5Constant md5Constant = new MD5Constant(10, list);

        //通过当登录人的信息来计算该用户被路由到 哪个节点了
        for (String user : users) {
            String name = String.format("%s:%d", user, System.currentTimeMillis());
            VirtualNode virtualNode = md5Constant.route(name);
            String key = virtualNode.getPyhsicalNode().getKey();
            countMap.get(key).incrementAndGet();
        }
        System.out.println(countMap);
    }

}
